using CfgTable;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    /// <summary>
    /// 更新位置
    /// 考虑到要在主界面小地图和地图主界面之间复用
    /// 做成一个组件 当前需要使用地图的UI只需要实现IMapContainer
    /// UIN_MapRoot生命周期不依赖UIWindow
    /// </summary>
    public class UIMapRoot : UIBaseNode
    {
        static UIMapRoot()
        {
            _staticLayers = new HashSet<int>();
            var enumValues = Enum.GetValues(typeof(StaticLayerType));
            for (var i = 1; i < enumValues.Length; i++)
            {
                var value = (int)enumValues.GetValue(i);
                _staticLayers.Add(value);
            }
        }

        #region Instances

        public UIMapRoot(int regionCid)
        {
            this.regionCid = regionCid;
            config = TableCenter.regionInstance.Get(regionCid);
            areaType = (GameplayAreaType)config.GameArea;
        }

        #endregion

        #region Exposed

        public int regionCid { get; }

        public GameplayAreaType areaType { get; }

        public CfgRegionInstance config { get; }

        public bool updatable { get; set; }

        public float factor { get; private set; }

        public IMapContainer curContainer => _containers.TryPeek(out var result) ? result : null;

        public void AttachTo(IMapContainer container)
        {
            if (_containers.TryPeek(out var prevContainer))
            {
                prevContainer.OnMapDetach();
            }

            _containers.Push(container);
            DoAttach(container);
            container.OnMapAttach(this);
        }

        public void DetachFrom(IMapContainer container)
        {
            if (!_containers.TryPeek(out var top) || top != container)
            {
                return;
            }

            _containers.Pop();
            container.OnMapDetach();

            if (!_containers.TryPeek(out var prevContainer))
            {
                //如果没有前置container,说明已经没有地方会使用该地图，直接回收掉
                UIMapManager.Instance.UnloadMap(regionCid);
                return;
            }

            DoAttach(prevContainer);
            prevContainer.OnMapAttach(this);
        }

        public void SetScale(float scale)
        {
            transform.localScale = new Vector3(scale, scale, 1);
        }

        public void FocusTarget(int targetMcid, out UIBaseMapSymbol target)
        {
            target = null;
            if (targetMcid == 0)
            {
                return;
            }

            var symbols = ListPool<UIBaseMapSymbol>.Get();
            GetStaticLayerSymbols(ref symbols);
            target = symbols.FirstOrDefault(symbol => symbol.data.mcid == targetMcid);
            ListPool<UIBaseMapSymbol>.Put(symbols);

            if (target == null)
            {
                return;
            }

            Focus(PositionWorldToMap(target.data.worldPosition), false);
        }

        public void FocusPlayer(bool rotate)
        {
            var playerAvatarEntity = GameplayAreaManager.Instance.curSceneArea.GetPlayerAvatarEntity();
            var playerPos = PositionWorldToMap(playerAvatarEntity.transform.position);
            Focus(playerPos, rotate);
        }

        private void Focus(Vector2 mapPos, bool rotate)
        {
            //玩家位置相对中心的偏移转化为pivot
            var pivotOffset = new Vector2((mapPos.x + _offset.x) / transform.sizeDelta.x,
                (mapPos.y + _offset.y) / transform.sizeDelta.y);
            var newPivot = Vector2.one * 0.5f + pivotOffset;
            transform.pivot = newPivot;
            transform.localPosition = Vector3.zero;
            if (!rotate)
            {
                transform.rotation = Quaternion.identity;
                return;
            }

            _mainCamera ??= Camera.main;
            if (_mainCamera == null)
            {
                transform.rotation = Quaternion.identity;
            }
            else
            {
                var cameraRotation = _mainCamera.transform.rotation.eulerAngles.y;
                //有的地图场景被旋转了180度，需要修正
                transform.rotation = Quaternion.Euler(0, 0, cameraRotation + (factor > 0 ? 0 : 180));
            }
        }

        private void ReleaseAll()
        {
            foreach (var layer in _mapLayers.Values)
            {
                ClassPool<MapLayer>.Put(layer);
            }

            DictionaryPool<int, MapLayer>.Put(_mapLayers);
            _mapLayers = null;

            _mainCamera = null;
        }

        public void ClearSymbols()
        {
            foreach (var kvp in _mapLayers)
            {
                if (kvp.Key is (int)StaticLayerType.FogLayer or (int)StaticLayerType.PlayerLayer)
                {
                    continue;
                }

                kvp.Value.Clear();
            }
        }

        public UIBaseMapSymbol AddSymbol(object rawData, int specifiedCid = 0)
        {
            var newSymbol = UIMapManager.Instance.GetSymbol(rawData, specifiedCid);
            var layer = GetLayer(newSymbol.data);
            layer?.AddSymbol(newSymbol);
            return newSymbol;
        }

        public void RemoveAllSymbolByEntityId(int entityId)
        {
            foreach (var mapLayer in _mapLayers.Values)
            {
                mapLayer.RemoveSymbol(entityId);
            }
        }

        private void RemoveAllSymbolByMapUICid(int mapUICid)
        {
            var symbols = ListPool<UIBaseMapSymbol>.Get();
            foreach (var mapLayer in _mapLayers.Values)
            {
                symbols.Clear();
                symbols.AddRange(mapLayer.GetAllSymbol());
                foreach (var symbol in symbols)
                {
                    if (symbol.data.config.Id == mapUICid)
                    {
                        mapLayer.RemoveSymbol(symbol.data.entityId);
                    }
                }
            }

            ListPool<UIBaseMapSymbol>.Put(symbols);
        }

        /// <summary>
        /// 根据mapUICid查找symbol的方法,支持任意数量mapUICid,params关键字有gc
        /// </summary>
        private void FindAllSymbolByMapUICid(ref List<UIBaseMapSymbol> result, params int[] mapUICids)
        {
            var symbols = ListPool<UIBaseMapSymbol>.Get();
            foreach (var mapLayer in _mapLayers.Values)
            {
                symbols.Clear();
                symbols.AddRange(mapLayer.GetAllSymbol());
                foreach (var symbol in symbols)
                {
                    foreach (var mapUICid in mapUICids)
                    {
                        if (symbol.data.config.Id == mapUICid)
                        {
                            result.Add(symbol);
                            break;
                        }
                    }
                }
            }
            ListPool<UIBaseMapSymbol>.Put(symbols);
        }

        /// <summary>
        /// 根据mapUICid查找symbol的方法,仅支持单个mapUICid,无gc
        /// </summary>
        private void FindAllSymbolByMapUICid(ref List<UIBaseMapSymbol> result, int mapUICid)
        {
            var symbols = ListPool<UIBaseMapSymbol>.Get();
            foreach (var mapLayer in _mapLayers.Values)
            {
                symbols.Clear();
                symbols.AddRange(mapLayer.GetAllSymbol());
                foreach (var symbol in symbols)
                {
                    if (symbol.data.config.Id == mapUICid)
                    {
                        result.Add(symbol);
                    }
                }
            }
            ListPool<UIBaseMapSymbol>.Put(symbols);
        }


        public IMapSymbolData GetNearestCheckPointMcid(Vector3 position)
        {
            IMapSymbolData result = null;
            IMapSymbolData spawnPoint = null;
            var minDistance = float.MaxValue;

            var symbols = ListPool<UIBaseMapSymbol>.Get();
            GetStaticLayerSymbols(ref symbols);

            foreach (var symbol in symbols)
            {
                if (symbol.data.config.Type == MapUiEntityType.SpawnPoint)
                {
                    spawnPoint = symbol.data;
                    continue;
                }

                if (!symbol.data.isCheckPointActive)
                {
                    continue;
                }

                var distance = Vector3.Distance(position, symbol.data.worldPosition);
                if (distance < minDistance)
                {
                    result = symbol.data;
                    minDistance = distance;
                }
            }

            ListPool<UIBaseMapSymbol>.Put(symbols);
            return result ?? spawnPoint;
        }

        #endregion

        #region Override

        /// <summary>
        /// 不使用UIMapManager的ResLoader
        /// MapRoot的地图图片会占用较大空间，需要及时释放
        /// 每个UIMapRoot有单独的ResLoader，使得MapRoot使用的地图图片能随UIMapRoot的Dispose释放
        /// </summary>
        public override ResLoader resLoader { get; } = ResLoader.Create();
        private UIMsgDispatcher uiMsgDispatcher => this._msgDispatcher as UIMsgDispatcher;

        protected override void BeforeInit()
        {

        }

        protected override void OnInit()
        {
            _mainCamera = Camera.main;
            ManagerCenter.MonoDriver.updateEvent += OnUpdate;
            _mapLayers = DictionaryPool<int, MapLayer>.Get();

            if (transform.GetChild(0) is not RectTransform coordinateRT) return;
            InitStaticLayers(coordinateRT);
            _offset = coordinateRT.transform.localPosition;
            if (transform.GetChild(1) is not RectTransform mapConfigRT) return;
            factor = mapConfigRT.localScale.z;
        }

        protected override void OnShow()
        {

        }

        protected override void OnHide()
        {

        }

        protected override void AddMsgListeners()
        {
            uiMsgDispatcher.AddListener<int>(MsgEventType.QuestStartTrack, OnTrackQuestChange);
        }

        protected override void OnDispose()
        {
            resLoader.Put2Pool();
            ManagerCenter.MonoDriver.updateEvent -= OnUpdate;
            ReleaseAll();
        }

        private void InitStaticLayers(RectTransform parent)
        {
            var enumValues = Enum.GetValues(typeof(StaticLayerType));
            var enumNames = Enum.GetNames(typeof(StaticLayerType));
            for (var i = 1; i < enumValues.Length; i++)
            {
                var value = (int)enumValues.GetValue(i);
                var layer = ClassPool.Get<MapLayer>();
                var layerTrans = parent.transform.Find(enumNames[i]);
                if (layerTrans == null)
                {
                    continue;
                }

                layer.Bind(this, layerTrans.gameObject);
                _mapLayers.Add(value, layer);
            }
            //todo by xg: temp code need to be removed
            GetLayer((int)StaticLayerType.FogLayer).root.SetActive(false);
        }

        //给定entityId mapUIEntityCid 找到对应layer
        private MapLayer GetLayer(IMapSymbolData data)
        {
            if (!_mapLayers.TryGetValue(data.layer, out var mapLayer))
            {
                mapLayer = AddDynamicMapLayer(data.layer);
            }

            return mapLayer;
        }

        public MapLayer GetLayer(int layer)
        {
            return _mapLayers.GetValueOrDefault(layer);
        }

        //动态层创建
        private MapLayer AddDynamicMapLayer(int layer)
        {
            var mapLayer = ClassPool<MapLayer>.Get();
            var layerRoot = CreateLayerRoot(layer);
            mapLayer.Bind(this, layerRoot);
            _mapLayers[layer] = mapLayer;
            return mapLayer;
        }

        //获取最近的layer,找到插入位置
        private GameObject CreateLayerRoot(int layer)
        {
            var nearestLayer = 0;
            GameObject nearestRoot = null;
            var minOffset = int.MaxValue;
            foreach (var kvp in _mapLayers)
            {
                var offset = kvp.Key - layer;
                offset = offset > 0 ? offset : -offset;
                if (offset >= minOffset) continue;
                nearestLayer = kvp.Key;
                nearestRoot = kvp.Value.root;
                minOffset = offset;
            }

            if (nearestRoot == null)
            {
                return nearestRoot;
            }

            var newLayerRoot = new GameObject
            {
                name = "Layer" + layer
            };
            newLayerRoot.AddComponent<RectTransform>();
            newLayerRoot.transform.SetParent(nearestRoot.transform.parent);
            newLayerRoot.transform.localPosition = Vector3.zero;
            newLayerRoot.transform.localScale = Vector3.one;
            if (nearestLayer > layer)
            {
                newLayerRoot.transform.SetSiblingIndex(nearestRoot.transform.GetSiblingIndex());
            }
            else
            {
                newLayerRoot.transform.SetSiblingIndex(nearestRoot.transform.GetSiblingIndex() + 1);
            }

            return newLayerRoot;
        }

        protected override void OnUpdate(float dt, float unscaledDt)
        {
            if (!updatable)
            {
                return;
            }

            var curArea = GameplayAreaManager.Instance.curSceneArea;
            if (!curArea.isSceneReady)
            {
                return;
            }

            foreach (var layer in _mapLayers.Values)
            {
                layer.Update(dt, unscaledDt);
            }

            if (_tracer != null)
            {
                foreach (var kvp in _mapLayers)
                {
                    var follow = kvp.Value.GetSymbol(_tracer.data.entityId);
                    if (follow == null) continue;
                    follow.transform.localPosition = _tracer.transform.localPosition;
                }
            }
        }

        #endregion

        #region Methods

        private void DoAttach(IMapContainer container)
        {
            gameObject.SetActive(true);
            transform.SetParent(container.containerRT);
            transform.localScale = Vector3.one;
            transform.localPosition = Vector3.zero;
        }

        public void CreatePlayer()
        {
            var playerAvatarEntity = GameplayAreaManager.Instance.curSceneArea.GetPlayerAvatarEntity();
            var player = UIMapManager.Instance.GetSymbol(playerAvatarEntity);
            var layer = GetLayer(player.data);
            layer.AddSymbol(player);
        }

        public void CreateFog()
        {
            var fogEntities = ListPool<TViewEntity>.Get();
            GameScenePlayUtil.GetAllMapFog(fogEntities);
            foreach (var fogEntity in fogEntities)
            {
                var newSymbol = UIMapManager.Instance.GetSymbol(fogEntity);
                var layer = GetLayer((int)StaticLayerType.FogLayer);
                layer?.AddSymbol(newSymbol);
            }

            ListPool<TViewEntity>.Put(fogEntities);
        }

        #endregion

        #region Utils

        public Vector2 PositionWorldToMap(Vector3 worldPos)
        {
            return new Vector2(worldPos.x * factor, worldPos.z * factor);
        }

        /// <summary>
        /// 世界和UI手系不同，取反
        /// 有的测试场景旋转了180度，需要修正
        /// </summary>
        public Quaternion RotationWorldToMap(Quaternion rotation)
        {
            var mapRotation = transform.rotation.eulerAngles.z;
            //按理是只用加mapRotation,但是mapRotation在处理地图factor < 0的情况时多加了180让地图表现正常,因此此处还需要再加180让图例表现正常
            return Quaternion.Euler(0, 0, -rotation.eulerAngles.y + mapRotation + (factor > 0 ? 0 : 180));
        }

        private void OnTrackQuestChange(int questCid)
        {
            RefreshAllTracer();
        }


        private void ClearTracer()
        {
            RemoveAllSymbolByMapUICid(TableCenter.mapUiEntity.TracerCid);
            RemoveAllSymbolByMapUICid(TableCenter.mapUiEntity.QuestTargetCid);
            RemoveAllSymbolByMapUICid(TableCenter.mapUiEntity.QuestAreaCid);
        }

        public void RefreshAllTracer()
        {
            ClearTracer();
            TraceQuest();
            TraceEntity();
        }

        private void TraceQuest()
        {
            var targets = ListPool<TViewEntity>.Get();
            GameScenePlayUtil.GetQuestTrackEntities(targets, out var targetRegion);

            if (targetRegion != regionCid)
            {
                ListPool<TViewEntity>.Put(targets);
                return;
            }

            var questCfg = TableCenter.quest.Get(GameDataCenter.PlayerData.quest.trackQuestCid);
            var mapUIEntityCid = (questCfg.TargetRangeSize?.FirstOrDefault() ?? 0) != 0
                ? TableCenter.mapUiEntity.QuestAreaCid
                : TableCenter.mapUiEntity.QuestTargetCid;
            foreach (var target in targets)
            {
                AddSymbol(target, mapUIEntityCid);
            }

            ListPool<TViewEntity>.Put(targets);
        }

        public void ChangeTraceEntity()
        {
            RemoveAllSymbolByMapUICid(TableCenter.mapUiEntity.TracerCid);
            TraceEntity();
        }


        private void TraceEntity()
        {
            _tracer = null;
            var tracingEntity = GameplayAreaManager.Instance.curSceneArea?.GetTrackingEntity();
            if (tracingEntity == null)
            {
                return;
            }

            var tracingEntityId = tracingEntity.id;
            foreach (var kvp in _mapLayers)
            {
                //重绘被追踪的symbol以刷新其maskable
                kvp.Value.GetSymbol(tracingEntityId)?.Refresh();
            }

            _tracer = AddSymbol(tracingEntity, TableCenter.mapUiEntity.TracerCid);
        }

        private void GetStaticLayerSymbols(ref List<UIBaseMapSymbol> result)
        {
            foreach (var layerId in _staticLayers)
            {
                if (_mapLayers.TryGetValue(layerId, out var layer))
                {
                    result.AddRange(layer.GetAllSymbol());
                }
            }
        }

        #endregion

        #region Field

        private Vector2 _offset;

        private readonly Stack<IMapContainer> _containers = new();

        private Dictionary<int, MapLayer> _mapLayers;

        private static readonly HashSet<int> _staticLayers;

        private Camera _mainCamera;

        private UIBaseMapSymbol _tracer;

        #endregion
    }
}